﻿using System;
using System.Collections.Generic;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Newtonsoft.Json;
using YacGE.Script;

namespace YacGE.Map
{
    [JsonObject(MemberSerialization.OptIn)]
    public class MapObject
    {
        private Vector2 _position;

        [JsonProperty]
        public Vector2 Position
        {
            get { return _position; }
            set { _position = value; }
        }

        private Vector2 _size;

        [JsonProperty]
        public Vector2 Size
        {
            get { return _size; }
            set { _size = value; }
        }

        private Vector2 _velocity;

        [JsonProperty]
        public Vector2 Velocity
        {
            get { return _velocity; }
            set { _velocity = value; }
        }

        [JsonProperty]
        public string RenderImageLocation { get; set; }

        [JsonProperty]
        public string ObjectName { get; set; }

        [JsonProperty]
        public Dictionary<string, object> Properties { get; set; }

        [JsonProperty]
        public List<string> UpdateFunctionNameList { get; set; }

        [JsonProperty]
        public Dictionary<Keys, List<string>> InputFunctionNameList { get; set; }

        [JsonProperty]
        public Dictionary<string, string> EventRecognitionFunctionNameList { get; set; }

        [JsonProperty]
        public Dictionary<string, List<string>> EventFunctionNameList { get; set; }

        private GraphicsDeviceManager _graphicsDeviceManager;
        private ScriptManager _scriptManager;
        private MapManager _mapManager;

        public Texture2D RenderImage;

        string _mapName;

        private readonly Dictionary<String, Func<object, object, object, bool>> _eventRecognitionFunctionList =
            new Dictionary<string, Func<object, object, object, bool>>();

        private readonly Dictionary<String, List<Func<object, object, object, bool>>> _eventFunctionList =
            new Dictionary<string, List<Func<object, object, object, bool>>>();

        private readonly List<Func<object, object, object, bool>> _updateFunctionList =
            new List<Func<object, object, object, bool>>();

        private readonly Dictionary<Keys, List<Func<object, object, object, bool>>> _inputFunctionList =
            new Dictionary<Keys, List<Func<object, object, object, bool>>>();

        public void Initialize(GraphicsDeviceManager graphicsDeviceManager, ScriptManager scriptManager,
                               MapManager mapManager, string mapName)
        {
            _mapName = mapName;
            _graphicsDeviceManager = graphicsDeviceManager;
            _scriptManager = scriptManager;
            _mapManager = mapManager;

            if (RenderImageLocation != null)
            {
                using (Stream stream = File.OpenRead(@"Maps/"+ _mapName + "/res/" + RenderImageLocation))
                {
                    RenderImage = Texture2D.FromStream(graphicsDeviceManager.GraphicsDevice, stream).ConvertToPreMultipliedAlpha();
                }
            }

            foreach (var functionName in UpdateFunctionNameList)
            {
                if (_scriptManager.HasFunction(functionName))
                    _updateFunctionList.Add(_scriptManager.GetFunction(functionName));
            }

            foreach (var keyValuePair in InputFunctionNameList)
            {
                if (_inputFunctionList.ContainsKey(keyValuePair.Key))
                    _inputFunctionList.Remove(keyValuePair.Key);

                _inputFunctionList.Add(keyValuePair.Key, new List<Func<object, object, object, bool>>());

                foreach (var functionName in keyValuePair.Value)
                {
                    if (_scriptManager.HasFunction(functionName))
                        _inputFunctionList[keyValuePair.Key].Add(_scriptManager.GetFunction(functionName));
                }
            }

            foreach (var keyValuePair in EventRecognitionFunctionNameList)
            {
                if (_eventRecognitionFunctionList.ContainsKey(keyValuePair.Key))
                    _eventRecognitionFunctionList.Remove(keyValuePair.Key);

                if (_scriptManager.HasFunction(keyValuePair.Value))
                    _eventRecognitionFunctionList.Add(keyValuePair.Key, _scriptManager.GetFunction(keyValuePair.Value));
            }

            foreach (var keyValuePair in EventFunctionNameList)
            {
                if (_eventFunctionList.ContainsKey(keyValuePair.Key))
                    _eventFunctionList.Remove(keyValuePair.Key);

                _eventFunctionList.Add(keyValuePair.Key, new List<Func<object, object, object, bool>>());

                foreach (var functionName in keyValuePair.Value)
                {
                    if (_scriptManager.HasFunction(functionName))
                        _eventFunctionList[keyValuePair.Key].Add(_scriptManager.GetFunction(functionName));
                }
            }
        }

        public bool Update(GameTime gameTime)
        {
            foreach (var func in _updateFunctionList)
                if (!func(this, _mapManager, gameTime))
                    return false;

            foreach (var key in _mapManager.CurrentKeyboardState.GetPressedKeys())
            {
                if (!_inputFunctionList.ContainsKey(key))
                    continue;

                foreach (var func in _inputFunctionList[key])
                {
                    func(this, _mapManager, gameTime);
                }
            }

            foreach (var recognizeSet in _eventRecognitionFunctionList)
            {
                if (!recognizeSet.Value(this, _mapManager, gameTime)) continue;
                foreach (var eventFunction in _eventFunctionList[recognizeSet.Key])
                    if (!eventFunction(this, _mapManager, gameTime))
                        return false;
            }
            return true;
        }

        public void Draw(SpriteBatch spriteBatch)
        {
            if (RenderImage != null)
            {
                spriteBatch.Draw(RenderImage, _position - _mapManager.GetCameraPosition(), Color.White);
            }
        }

        #region Access Members

        public void SetPosition(float x, float y)
        {
            _position.X = x;
            _position.Y = y;
        }

        public void SetSize(float x, float y)
        {
            _size.X = x;
            _size.Y = y;
        }

        public void SetVelocity(float x, float y)
        {
            _velocity.X = x;
            _velocity.Y = y;
        }

        #endregion
    }
}